/*
 * System test of dominion.c
 * Adam Cohen
 * CS 362 2/23/14
 */

#include "dominion.h"
#include "dominion_helpers.h"
#include "rngs.h"
#include "interface.h"
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

//helper functions
int genKCards(int * );
int * getMove (struct gameState * g);
int * getChoices(int * cardNumber, int * kingdomCards, struct gameState * g);
int checkDecks(struct gameState * g, int players);
void phaseNumToName(int phase, char *name);
int calcMoney(struct gameState * g);
char* getCard(int cardNo);

int main(int argc, char** argv){
	
  //iterator to play 3 games
  int globalIter;

  //for output to file
  FILE *f;
  f = fopen("gameResults.out", "w");  
  fprintf(f, "Dominion Results");
  fclose(f);

  //play 3 games 
  for (globalIter = 1; globalIter < 4; globalIter++){
    
    //local vars
    int errorCount = 0; 
    int randomSeed = globalIter;
    int i;
    int * kingdomCards = malloc(sizeof(int) * 10);
    int players;
    struct gameState g; 

    //seed random generator
    SelectStream(1);
    PutSeed((long)randomSeed);

    //identify game
    printf("\nGame: %d\n\n", globalIter);

    //generate kingdom cards
	  if (genKCards(kingdomCards) == 0){
		  f = fopen("gameResults.out", "a");  
      fprintf(f, "\n\nNEW GAME\n\nKing Cards:");      
      for (i = 0; i < 10; i++){
			 fprintf(f, "Kingcard[%d] = %d\n", i, kingdomCards[i]);
		  }
      fclose(f); 
	  }
    //determine player count  
    players = (floor(Random() * (MAX_PLAYERS - 1)) + 2); //players: 2, 3, or 4
    
    //initialize game using function in dominion.c for better system testing
    initializeGame(players, kingdomCards, randomSeed, &g);  

    //begin error reporting
    printf("Errors occurred as follows:\n\n");

    //check that players start out with right amount of cards
    checkDecks(&g, players);

    //beginGameplay
  
    while(!(isGameOver(&g))){
    
      f = fopen("gameResults.out", "a");  
      fprintf(f, "\nplayer %d's turn:", g.whoseTurn);
      fclose(f);

      printf("\nplayer %d's turn:", g.whoseTurn);    
        //variable to track playable action cards
        int action;
    
      //play all available action cards
      do {
        //randomly determine play to make 
        int * move = getMove(&g);
        action = 1;
        //if no action cards in hand, cannot make play, just buy
        if (move[0] == -1 || g.numActions == 0){
          action = 0;
          free(move);
        }
        //otherwise, attempt action play, reporting failures to output
        if (action){
          int * results = getChoices(move, kingdomCards, &g);
          int res = playCard(move[1], results[0], results[1], results[2], &g);
        if (res == -1){
          errorCount++;
          printf("\nPlaying %s card by player %d failed\n"
            "ignoring and moving on to buy stage to avoid game stall\n", getCard(move[0]), whoseTurn(&g));
          g.numActions = 0;
        }
        else if (handCard(move[1], &g) == move[0]){
          errorCount++;
          printf("\nDiscard of %s card by player %d likely failed\n"
            "ignoring and moving on to buy stage\n", getCard(move[0]), whoseTurn(&g));
          g.numActions = 0;
        }
        else{
          f = fopen("gameResults.out", "a");  
          fprintf(f, "player %d played %s\n", whoseTurn(&g), getCard(move[0]));
          fclose(f);
        }
      free(move);
      }
    }
    while (action);

    //buy phase
    int tryCount = 0;
    int successCount = 0;

    while(g.numBuys > 0){    
      //determine amt of money available
      int money = calcMoney(&g);       
      int valid = 1;

      //generate card to purchase
      int purchase;    
      purchase = floor(Random() * (treasure_map + 1));

      //validate card generated
      if (!(g.supplyCount[purchase] > 0 && money >= getCost(purchase))){
        valid = 0;
        tryCount++;
      }

      //if too many tries have failed, skip buy to avoid stall
      if (tryCount - successCount > 10){
        errorCount++;
        printf("Player %d had an error completing a buy operation", whoseTurn(&g));
        g.numBuys = 0;
      }
      //if card is valid, attempt to buy, reporting errors
      if (valid == 1){
        if (buyCard(purchase, &g) == 0){
          f = fopen("gameResults.out", "a");      
          fprintf(f, "Player %d bought %s\n", whoseTurn(&g), getCard(purchase));
          fclose(f);
          money -= getCost(purchase);
          successCount++;
          tryCount++;
        }
        else{
          errorCount++;
          printf("Player %d's attempt to buy %s failed\n", whoseTurn(&g), getCard(purchase));
          tryCount++;

      }
    }
    
  }


  //next player's turn
  endTurn(&g);
  
  }

  //when game is over, print results.
  printf("\n\nFinished game");
  if (errorCount > 0){
    printf(" with errors.");
    printf("\nTotal Errors: %d\n\n", errorCount);
  } 
  
  f = fopen("gameResults.out", "a");  
  
  fprintf (f, "Final Scores:\n");
  
  for (i = 0; i < players; i++){
    fprintf (f, "Player %d: %d\n", i, scoreFor(i, &g));
  }

  fclose(f);


  printf("-----------------------------------------------\n");
 }

 return 0;
}

/*
 * This function generates the choice variables needed for Playcard/cardEffect, consistent with Dominion rules
 *
 */
int * getChoices(int * cardNumandPos, int * kingdomCards, struct gameState * g){
  int * choices = malloc(sizeof(int) * 3);
  choices[0] = -1;
  choices[1] = -1;
  choices[2] = -1;
  switch (cardNumandPos[0]){
    
    case feast:
    {
      //randomly generated supply pile.  20 total supply piles
      int valid = 0;
      int supply = -1;
      while (!valid){
        supply = (floor(Random() * (treasure_map + 1)));
        //printf("Supply: %d\n", supply);
        //card cannot be more expensive than 5
        if (getCost(supply) <= 5){        
          if (supply < 7){
            valid = 1;
            break;
          }
          else{
            if (g->supplyCount[supply] > 0){
              valid = 1;
              break;
            }
          }
        }
      }
      choices[0] = supply;

      return choices;
    }
    case mine:
    {
      //determine least valuable money card in hand to trash.
      int i = 0; //loop counter
      int discard = -1; //position to discard
      int value = 0; //value of discarded card
      for (i = 0; i < numHandCards(g); i++){
        int cur = handCard(i, g);
        if (cur == copper || cur == silver || cur == gold){
          if (discard == -1){
            discard = i;
            value = cur;
          }
          else if (cur < value){
            discard = i;
            value = cur;
          }
        }
      }

      int pickup = -1;
      //swap copper for silver
      if (value == 4){
        pickup = 5;
      }
      //swap silver for gold
      else if (value == 5){
        pickup = 6;
      }
      //swap gold for gold
      else if (value == 6){
        pickup = 6;
      }
      //if no treasure cards, nothing to discard. -1 value is used.
      choices[0] = discard;
      choices[1] = pickup;
      

      return choices;
    }
    case remodel:
    {
      int valid = 0;
      int card = -1;
      while (!valid){
        //generate random card to discard, excluding card to be played
        card = floor(Random() * (numHandCards(g) + 1));
        if (card != cardNumandPos[1]){
          valid = 1;
        }
      }
      //draw card from random supply pile costing no more than 2 more than discarded.
      int cardNum = handCard(card, g);
      valid = 0;
      int supply = -1;
      while (!valid){
        supply = (floor(Random() * ((treasure_map + 1) - 9)));
        //card cannot be more expensive than discarded + 2
        if (g->supplyCount[supply] > 0){
          if (getCost(supply - cardNum) <= 2){        
            valid = 1;
            break;
          }
        }
      }

      choices[0] = card;
      choices[1] =supply;
      

      return choices;
    }
    case baron:
    {
      int discardEstate = 0;
      
      //determine whether hand contains any estate to discard
      int hasEstate = 0;
      int i;
      for (i = 0; i < numHandCards(g); i++){
        if (handCard(i, g) == estate){
          hasEstate = 1;
          break;
        }
      }

      if(hasEstate){
        discardEstate = floor(Random() * 2); //random true/false
      }

      choices[0] = discardEstate;  
    }
    case minion:
    {
      int choice = floor(Random() * 2);
      choices[choice] = 1;
      return choices;
    }
    case steward:
    {
      int choice = floor(Random() * 3);
      choices[choice] = 1;
      return choices;
    }
       
    case ambassador:
    { 
        //discard random card other than that being played.
        //if more than one of that card is present, randomize whether to trash 2 of them
        int valid = 0;
        int cardPos;
        while (!valid){
          cardPos = floor(Random() * (numHandCards(g)));
          if (cardPos != cardNumandPos[1]){
              valid = 1;
              choices[0] = cardPos;
          }
        }
        //if more than one of that card is present, randomize whether to trash 2 of them
        int i;
        int counter = 1;
        for (i = 0; i < numHandCards(g); i++){
          if (i != cardPos){
            if (handCard(i, g) == handCard(cardPos, g)){
              counter++;
              break;            
            }
          }
        }
        if (counter > 1){
          int trashNum = floor(Random() * 2) + 1;
          choices[1] = trashNum;
        }
        else{
          choices[1] = 1;
        }
      return choices;
      }
           

          
    case embargo:
    {
      //pick a random supply pile to embargo.
      //pile must not be empty
      int valid = 0;
      int supply = -1;
      while (!valid){
        supply = (floor(Random() * (treasure_map + 1)));
          if (g->supplyCount[supply] > 0){         
            valid = 1;
        }
      }
      choices[0] = supply;
      return choices;
    }          
    case salvager:
    {
        //trash random card
        int valid = 0;
        while (!valid){
          int cardPos = floor(Random() * (numHandCards(g)));
          if (cardPos != cardNumandPos[1]){
              valid = 1;
              choices[0] = cardPos;
          }
        }
        return choices;
    
    }
   
}
return choices; 
}


/*
 * This functio randomly generates and validates a move.
 */
int * getMove (struct gameState * g){  
  //int handCount = 0;
  int i;  
  int actionCardCount = 0;
  int * cardToPlay = malloc(sizeof(int) * 2); //array to store action card and its position in hand
  cardToPlay[0] = -1;
  cardToPlay[1] = -1;

  //count action cards
  for (i = 0; i < numHandCards(g); i++){
    int card = handCard(i, g);
    if (card >= adventurer && card != gardens){
      actionCardCount++;
      //printf("card: %d", card); 
    }
  }

  //printf("\nActionCount = %d", actionCardCount);

  //no action cards to play; move on to buy
  if (actionCardCount == 0){
    int * result = malloc (sizeof(int) * 2);
    result[0] = -1;
    result[1] = -1;
    return result;
  }
  

  int valid = 0;
  while (!valid){
    int randCardToPlay = floor(Random() * actionCardCount);//pick a random action card to play

    

    int cardPicker = 0; //tracks action cards in deck

    //iterate through hand to find randomly selected action card
    for (i = 0; i < numHandCards(g); i++){
      int card = handCard(i, g);
      //check if card is action card or not
      if (card >= adventurer && card != gardens){      
        //if card is action card, is it the randomly generated action card (from action card 1 to n)?
        if(cardPicker == randCardToPlay){

          //after passing treasure_map test, card is valid.  store in array to return and stop looking.
          cardToPlay[0] = card;
          cardToPlay[1] = i;
          valid = 1;
          break;
        }    
        //if card is not randomly chosen action card, increment action card counter and move on.  
        else{
          cardPicker++;
        }
      } 
    }
   
  }
  return cardToPlay;
}

/*
 * This function calculates the money a player has in his/her hand.
 *
 */
int calcMoney(struct gameState * g){
    int i, money = 0;
    for (i = 0; i < numHandCards(g); i++) {
    if (handCard(i, g) == copper)
    money++;
      else if (handCard(i, g) == silver)
    money += 2;
      else if (handCard(i, g) == gold)
    money += 3;
    }

    return money;
}  

/*
 * Random king card generator
 */
int genKCards(int * kingCards){

  int numSelected = 0; 
  int i;

  while(numSelected < NUM_K_CARDS) {
    int used = 0;
    int card = floor(Random() * NUM_TOTAL_K_CARDS);
    if(card < adventurer) continue;
    for(i = 0; i < numSelected; i++) {
      if(kingCards[i] == card) {
	used = 1;
	break;
      }
    }
    if(used == 1) continue;
    kingCards[numSelected] = card;
    numSelected++;
  }
  return 0;
}


/*
 * converts phase number to name for printing (borrowed from existing dominion library)
 */
void phaseNumToName(int phase, char *name) {
  switch(phase){
  case ACTION_PHASE: strcpy(name,"Action");
    break;
  case BUY_PHASE: strcpy(name,"Buy");
    break;
  case CLEANUP_PHASE: strcpy(name,"Cleanup");
    break;  
  }
}

/*
 * validates generated decks at beginning of game to match dominion rules (7 coppers and 3 estates)
 */
int checkDecks(struct gameState * g, int players){
  
  int i;

  int * decks = malloc(sizeof(int)* players);
  for (i = 0; i < players; i++){
    decks[i] = 0;
  }

  
  for (i = 0; i < players; i++){
    int copperCounter = 0;
    int estateCounter = 0;
    int j;
      for (j = 0; j < g->deckCount[i]; j++){
        if (g->deck[i][j] == copper){
          copperCounter++;
        }
        else if(g->deck[i][j] == estate){
          estateCounter++;
        }
      }      

      if (copperCounter == 7 && estateCounter == 3 && g->deckCount[i] == 3){
        decks[i] = 1;
      }
    }
  
  int decksStatus = 1;
  for (i = 0; i < players; i++){
    if (decks[i] == 0){
      decksStatus = 0;
    }
  }
  if (decksStatus == 1){
    printf("DECK CHECK PASSED: Each player properly starts with a deck of 10 cards containing"
            "7 coppers and 3 estates\n");
  }
  else{
    printf("DECK CHECK FAILED: Each player did not start out with a deck containing 10 cards"
            "with 7 coppers and 3 estates\n");
  }
  return 0;
  
}

/*
 * utility function to convert card number to name for printing purposes.
 */
char* getCard(int cardNo){
    if (cardNo == curse){
        return "curse";
    }
    if (cardNo == estate){
        return "estate";
    }
    if (cardNo == duchy){
        return "duchy";
    }
    if (cardNo == province){
        return "province";
    }
    if (cardNo == copper){
        return "copper";
    }
    if (cardNo == silver){
        return "silver";
    }
    if (cardNo == gold){
        return "gold";
    }
    if (cardNo == adventurer){
        return "adventurer";
    }
    if (cardNo == council_room){
        return "council_room";
    }
    if (cardNo == feast){
        return "feast";
    }
    if (cardNo == gardens){
        return "gardens";
    }
    if (cardNo == mine){
        return "mine";
    }
    if (cardNo == remodel){
        return "remodel";
    }
    if (cardNo == smithy){
        return "smithy";
    }
    if (cardNo == village){
        return "village";
    }
    if (cardNo == baron){
        return "baron";
    }
    if (cardNo == great_hall){
        return "great_hall";
    }
    if (cardNo == minion){
        return "minion";
    }
    if (cardNo == steward){
        return "steward";
    }
    if (cardNo == tribute){
        return "tribute";
    }
    if (cardNo == ambassador){
        return "ambassador";
    }
    if (cardNo == cutpurse){
        return "cutpurse";
    }
    if (cardNo == embargo){
        return "embargo";
    }
    if (cardNo == outpost){
        return "outpost";
    }
    if (cardNo == salvager){
        return "salvager";
    }
    if (cardNo == sea_hag){
        return "sea_hag";
    }
    if (cardNo == treasure_map){
        return "treasure_map";
    }
    return "fail";
}




